package com.hzw.saas.web.app.access.controller;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.hzw.saas.api.hpc.IHpcAccountService;
import com.hzw.saas.api.hpc.IHpcAccountUserService;
import com.hzw.saas.api.hpc.bo.HpcAccountBo;
import com.hzw.saas.api.hpc.bo.HpcAccountUserBo;
import com.hzw.saas.api.product.ILicenseProductModuleService;
import com.hzw.saas.api.product.ILicenseProductService;
import com.hzw.saas.api.product.ILicenseUserService;
import com.hzw.saas.api.product.dto.LicenseProductBO;
import com.hzw.saas.api.product.dto.LicenseUserBO;
import com.hzw.saas.api.property.IPropertyService;
import com.hzw.saas.api.property.enmus.SysDictKeyEnum;
import com.hzw.saas.api.user.ICustomerService;
import com.hzw.saas.api.user.bo.CustomerBO;
import com.hzw.saas.common.config.annotation.SysLog;
import com.hzw.saas.common.config.util.PageParam;
import com.hzw.saas.common.security.utils.SecurityUtils;
import com.hzw.saas.common.util.Json;
import com.hzw.saas.web.app.access.dto.HpcConfigAccountInfoCryptDto;
import com.hzw.saas.web.app.access.dto.HpcConfigAccountInfoDto;
import com.hzw.saas.web.app.access.dto.LicenseProductStructDto;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiOperationSort;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import ma.glasnost.orika.MapperFacade;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * <p>
 *
 * </p>
 *
 * @author sonam
 * @since 2021/3/1 15:31
 */
@Slf4j
@Api(tags = "应用接入")
@RequestMapping
@RestController
@RequiredArgsConstructor
public class AppAccessController {

    @Resource(name = "hpcAccountUserServiceImpl")
    private IHpcAccountUserService hpcAccountUserService;
    @Resource(name = "hpcAccountServiceImpl")
    private IHpcAccountService hpcAccountService;
    @Resource(name = "licenseUserServiceImpl")
    private ILicenseUserService licenseProductUserService;
    @Resource(name = "licenseProductServiceImpl")
    private ILicenseProductService licenseProductService;
    @Resource(name = "licenseProductModuleServiceImpl")
    private ILicenseProductModuleService licenseProductModuleService;
    @Resource(name = "customerServiceImpl")
    private ICustomerService customerService;

    @Resource(name = IPropertyService.SYS_DICT_BEAN)
    private IPropertyService<SysDictKeyEnum> dictService;

    private final MapperFacade mapperFacade;

    /**
     * 产品系列编号 (目前只有prodId为6的模块License)
     */
    static String PRODCUT_SERIES_ID = "6";

    @SysLog(operation = "获取产品License", mode = 1, printResult = 1)
    @GetMapping("/license")
    @ApiOperation(value = "获取产品License信息")
    @ApiOperationSort(1)
    public ResponseEntity<List<LicenseProductStructDto>> productLicense() {
        String userId = SecurityUtils.getUser().getUserId();

        LicenseUserBO licenseUser = new LicenseUserBO();
        licenseUser.setUserId(userId);
        List<LicenseUserBO> licenseUserBOS = licenseProductUserService.searchLicenseUser(licenseUser);
        // 产品ID为PRODCUT_SERIES_ID的LicenseUserBO
        final List<LicenseUserBO> LicenseSeriesBos = new ArrayList<>();

        List<LicenseProductStructDto> licenseProductStructDtos = new ArrayList<>();
        licenseUserBOS.forEach(licenseUserBO -> {
            if (DateUtil.compare(licenseUserBO.getValidatedExpiredDate(), new Date()) < 0) {
                return;
            }
            String licenseId = licenseUserBO.getLicenseId();
            LicenseProductBO licenseProductBO = licenseProductService.getByLicenseId(licenseId);
            if (Objects.isNull(licenseProductBO)) {
                return;
            }

            LicenseProductStructDto licenseProductStructDto = new LicenseProductStructDto()
                .setLicenseId(licenseUserBO.getLicenseId())
                .setProductName(licenseProductBO.getProductName())
                .setProductVersion(licenseUserBO.getProductVersion())
                .setValidatedEffectiveDate(DateUtil.formatDate(licenseUserBO.getValidatedEffectiveDate()))
                .setValidatedExpiredDate(DateUtil.formatDate(licenseUserBO.getValidatedExpiredDate()))
                .setMaintenanceEffectiveDate(DateUtil.formatDate(licenseUserBO.getMaintenanceEffectiveDate()))
                .setMaintenanceExpiredDate(DateUtil.formatDate(licenseUserBO.getMaintenanceExpiredDate()))
                .setMaxCpuCore(licenseUserBO.getMaxCpuCore())
                .setMaxMemory(licenseUserBO.getMaxMemory())
                .setParallelModel(licenseUserBO.getParallelModel())
                .setProductType(licenseUserBO.getProductType())
                .setHpcUser(licenseUserBO.getHpcUser())
                .setReservedInfo1(licenseProductBO.getReservedInfo1())
                .setReservedInfo2(licenseProductBO.getReservedInfo2())
                .setReservedInfo3(licenseProductBO.getReservedInfo2());
            licenseProductStructDtos.add(licenseProductStructDto);

            // 找到目标productId先存一下
            if (PRODCUT_SERIES_ID.equals(licenseUserBO.getProductId())) {
                LicenseSeriesBos.add(licenseUserBO);
            }
        });

        // 获取当前用户的会员级别
        CustomerBO customerBO = customerService.getCustomerByUserId(userId);

        // 异常提前返回
        if (null == customerBO || StringUtils.isBlank(customerBO.getCustomerLevel())) {
            LicenseSeriesBos.clear();
            return ResponseEntity.ok(licenseProductStructDtos);
        }

        // 获取共享人列表（以及会员级别）
        List<CustomerBO> sharedCustomerBOS = hpcAccountUserService.listMainSharedCustomers(userId);

        // 取最大的会员级别
        int maxLevel = Integer.parseInt(customerBO.getCustomerLevel());
        for (CustomerBO customerBO1 : sharedCustomerBOS) {
            maxLevel = Math.max(maxLevel, Integer.parseInt(customerBO1.getCustomerLevel()));
        }
        log.debug(String.format("个人level: %s, 共享人最大level: %d", customerBO.getCustomerLevel(), maxLevel));

        // 根据会员级别添加目标产品模块的License
        List<LicenseProductBO> licenseProductBOS = licenseProductModuleService.listLicenseByLevel(PRODCUT_SERIES_ID, String.valueOf(maxLevel));
        licenseProductBOS.forEach(licenseProductBO -> {
            LicenseProductStructDto licenseProductStructDto = new LicenseProductStructDto()
                .setLicenseId(licenseProductBO.getLicenseId())
                .setProductName(licenseProductBO.getProductName())
                .setProductVersion(licenseProductBO.getProductVersion())
                .setMaxCpuCore(licenseProductBO.getMaxCpuCore())
                .setMaxMemory(licenseProductBO.getMaxMemory())
                .setParallelModel(licenseProductBO.getParallelModel())
                .setProductType(licenseProductBO.getProductType())
                .setHpcUser(licenseProductBO.getHpcUser())
                .setReservedInfo1(licenseProductBO.getReservedInfo1())
                .setReservedInfo2(licenseProductBO.getReservedInfo2())
                .setReservedInfo3(licenseProductBO.getReservedInfo2());

            // 将产品模块的License有效期设置为与产品本身一致
            if (!CollectionUtil.isEmpty(LicenseSeriesBos) && LicenseSeriesBos.size() == 1) {
                licenseProductStructDto
                    .setValidatedEffectiveDate(DateUtil.formatDate(LicenseSeriesBos.get(0).getValidatedEffectiveDate()))
                    .setValidatedExpiredDate(DateUtil.formatDate(LicenseSeriesBos.get(0).getValidatedExpiredDate()))
                    .setMaintenanceEffectiveDate(DateUtil.formatDate(LicenseSeriesBos.get(0).getMaintenanceEffectiveDate()))
                    .setMaintenanceExpiredDate(DateUtil.formatDate(LicenseSeriesBos.get(0).getMaintenanceExpiredDate()));
            }
            licenseProductStructDtos.add(licenseProductStructDto);
        });
        LicenseSeriesBos.clear();
        return ResponseEntity.ok(licenseProductStructDtos);
    }

    @GetMapping("/hpcs/connection")
    @SysLog(operation = "分页获取当前用户所有可用的超算账号及连接信息接口", mode = 1, printResult = 1)
    @ApiOperation(value = "分页获取当前用户所有可用的超算账号及连接信息接口")
    @ApiOperationSort(1)
    public ResponseEntity<IPage<HpcConfigAccountInfoDto>> hpcsConnection(PageParam pageParam) {
        IPage<HpcAccountUserBo> hpcAccountBoIPage = hpcAccountUserService.getUsableListByUserIdPage(pageParam, SecurityUtils.getUser().getUserId(), null, false);

        IPage<HpcConfigAccountInfoDto> hpcConfigAccountInfoDtoIPage = pageParam.convert();
        hpcConfigAccountInfoDtoIPage.setRecords(new ArrayList<>());

        hpcAccountBoIPage.getRecords().forEach(accountUserBo -> {
            String hpcAccountId = accountUserBo.getHpcAccountId();
            HpcAccountBo hpcAccountBo = hpcAccountService.getById(hpcAccountId);

            HpcConfigAccountInfoDto hpcConfigAccountInfoDto = mapperFacade.map(accountUserBo, HpcConfigAccountInfoDto.class);
            hpcConfigAccountInfoDto.setIp(hpcAccountBo.getHost());
            hpcConfigAccountInfoDto.setPort(hpcAccountBo.getPort());
            hpcConfigAccountInfoDto.setHpcAccount(hpcAccountBo.getHpcAccount());
            hpcConfigAccountInfoDto.setHpcPassword(hpcAccountBo.getHpcPassword());
            String[] split = hpcAccountBo.getHpcAccount().split("@");
            if (split.length >= 2) {
                hpcConfigAccountInfoDto.setHpcName(split[1]);
                hpcConfigAccountInfoDto.setHpcDesc(split[1]);
            }

            if (accountUserBo.getIsMain() == 1) {
                hpcConfigAccountInfoDto.setExpireDate(accountUserBo.getExpireTime());
            } else {
                hpcConfigAccountInfoDto.setExpireDate(accountUserBo.getSubExpireTime());
            }

            hpcConfigAccountInfoDtoIPage.getRecords().add(hpcConfigAccountInfoDto);
        });

        return ResponseEntity.ok(hpcConfigAccountInfoDtoIPage);
    }

    private final static String HPC_CRYPTO_MODULE = "HPC_CRYPTO";
    private final static String HPC_CRYPTO_PUBLIC_KEY = "publicKey";
    private final static String HPC_CRYPTO_PRIVATE_KEY = "privateKey";

    private final static String HPC_CRYPTO_API_NOTES = "该接口返回的主体数据使用AES加密(BASE64)，AES密钥使用RSA加密(BASE64)。" +
        "解密流程：" +
        "1. 使用BASE64解码AES密钥字符串" +
        "2. 使用RSA解密AES密钥" +
        "3. 使用BASE64解码数据主体" +
        "4. 使用解密后的AES密钥解密数据主体" +
        "5. 使用UTF-8编码数据主体，得到结果JSON字符串";

    @GetMapping("/hpcs/connection/crypt")
    @SysLog(operation = "分页获取当前用户所有可用的超算账号及连接信息接口(加密)", mode = 1, printResult = 1)
    @ApiOperation(value = "分页获取当前用户所有可用的超算账号及连接信息接口(加密)",notes = HPC_CRYPTO_API_NOTES)
    @ApiOperationSort(1)
    public ResponseEntity<HpcConfigAccountInfoCryptDto> hpcsConnectionCrypt(PageParam pageParam) {
        // 查询RSA公钥
        String publicKeyStr = dictService.getPropertyValue(HPC_CRYPTO_MODULE, HPC_CRYPTO_PUBLIC_KEY, "");
        Assert.notBlank(publicKeyStr, () -> new RuntimeException("未找到RSA公钥信息"));

        IPage<HpcAccountUserBo> hpcAccountBoIPage = hpcAccountUserService.getUsableListByUserIdPage(pageParam, SecurityUtils.getUser().getUserId(), null, false);

        IPage<HpcConfigAccountInfoDto> hpcConfigAccountInfoDtoIPage = pageParam.convert();
        hpcConfigAccountInfoDtoIPage.setRecords(new ArrayList<>());

        hpcAccountBoIPage.getRecords().forEach(accountUserBo -> {
            String hpcAccountId = accountUserBo.getHpcAccountId();
            HpcAccountBo hpcAccountBo = hpcAccountService.getById(hpcAccountId);

            HpcConfigAccountInfoDto hpcConfigAccountInfoDto = mapperFacade.map(accountUserBo, HpcConfigAccountInfoDto.class);
            hpcConfigAccountInfoDto.setIp(hpcAccountBo.getHost());
            hpcConfigAccountInfoDto.setPort(hpcAccountBo.getPort());
            hpcConfigAccountInfoDto.setHpcAccount(hpcAccountBo.getHpcAccount());
            hpcConfigAccountInfoDto.setHpcPassword(hpcAccountBo.getHpcPassword());
            String[] split = hpcAccountBo.getHpcAccount().split("@");
            if (split.length >= 2) {
                hpcConfigAccountInfoDto.setHpcName(split[1]);
                hpcConfigAccountInfoDto.setHpcDesc(split[1]);
            }

            if (accountUserBo.getIsMain() == 1) {
                hpcConfigAccountInfoDto.setExpireDate(accountUserBo.getExpireTime());
            } else {
                hpcConfigAccountInfoDto.setExpireDate(accountUserBo.getSubExpireTime());
            }

            hpcConfigAccountInfoDtoIPage.getRecords().add(hpcConfigAccountInfoDto);
        });

        String result = Json.toJsonString(hpcConfigAccountInfoDtoIPage);

        // 使用AES算法加密HPC数据主体
        byte[] aesKey = SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue()).getEncoded();
        SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.AES, aesKey);
        // 加密，输出为base64编码
        String resultCryptStr = aes.encryptBase64(result);

        // 使用RSA加密AES密钥
        RSA rsa = new RSA(null, publicKeyStr);
        // 加密，输出为base64编码
        String aesKeyCryptStr = rsa.encryptBase64(aesKey, KeyType.PublicKey);

        return ResponseEntity.ok(new HpcConfigAccountInfoCryptDto(resultCryptStr, aesKeyCryptStr));
    }

}
